home *** CD-ROM | disk | FTP | other *** search
/ Amiga Collections: Camelot / Camelot 078 (1990-06)(Swedish User Group of Amiga)(SE)(PD)[WB].zip / Camelot 078 (1990-06)(Swedish User Group of Amiga)(SE)(PD)[WB].adf / MSH / src / hansec.c < prev    next >
C/C++ Source or Header  |  1990-06-17  |  21KB  |  938 lines

  1. /*-
  2.  * $Id: hansec.c,v 1.30 90/06/04 23:17:02 Rhialto Rel $
  3.  * $Log:    hansec.c,v $
  4.  * Revision 1.30  90/06/04  23:17:02  Rhialto
  5.  * Release 1 Patch 3
  6.  * 
  7.  * HANSEC.C
  8.  *
  9.  * The code for the messydos file system handler.
  10.  *
  11.  * Sector-level stuff: read, write, cache, unit conversion.
  12.  * Other interactions (via MyDoIO) with messydisk.device.
  13.  *
  14.  * This code is (C) Copyright 1989,1990 by Olaf Seibert. All rights reserved.
  15.  * May not be used or copied without a licence.
  16. -*/
  17.  
  18. #include "dos.h"
  19. #include "han.h"
  20.  
  21. /*#undef HDEBUG              /**/
  22. #ifdef HDEBUG
  23. #   define    debug(x)  dbprintf x
  24. #else
  25. #   define    debug(x)
  26. #endif
  27.  
  28. struct MsgPort *DiskReplyPort;
  29. struct IOExtTD *DiskIOReq;
  30. struct IOStdReq *DiskChangeReq;
  31.  
  32. struct DiskParam Disk;
  33. byte           *Fat;
  34. short        FatDirty;    /* Fat must be written to disk */
  35.  
  36. short        error;        /* To put the error value; for Result2 */
  37. long        IDDiskState;    /* InfoData.id_DiskState */
  38. long        IDDiskType;    /* InfoData.id_DiskType */
  39. struct timerequest *TimeIOReq;    /* For motor-off delay */
  40. struct MinList    CacheList;    /* Sector cache */
  41. int        CurrentCache;    /* How many cached buffers do we have */
  42. int        MaxCache;    /* Maximum amount of cached buffers */
  43. long        CacheBlockSize; /* Size of disk block + overhead */
  44. ulong        BufMemType;
  45. int        DelayState;
  46. short        CheckBootBlock; /* Do we need to check the bootblock? */
  47.  
  48. word
  49. Get8086Word(Word8086)
  50. register byte  *Word8086;
  51. {
  52.     return Word8086[0] | Word8086[1] << 8;
  53. }
  54.  
  55. word
  56. OtherEndianWord(oew)
  57. word        oew;
  58. {
  59. /* INDENT OFF */
  60. #asm
  61.     move.w    8(a5),d0
  62.     rol.w    #8,d0
  63. #endasm
  64.     /* INDENT ON */
  65.     /*
  66.      * return (oew << 8) | ((oew >> 8) & 0xff);
  67.      */
  68. }
  69.  
  70. ulong
  71. OtherEndianLong(oel)
  72. ulong        oel;
  73. {
  74. /* INDENT OFF */
  75. #asm
  76.     move.l    8(a5),d0
  77.     rol.w    #8,d0
  78.     swap    d0
  79.     rol.w    #8,d0
  80. #endasm
  81.     /* INDENT ON */
  82.     /*
  83.      * return ((oel &       0xff) << 24) | ((oel &     0xff00) <<  8) |
  84.      * ((oel &   0xff0000) >>  8) | ((oel & 0xff000000) >> 24);
  85.      */
  86. }
  87.  
  88. void
  89. OtherEndianMsd(msd)
  90. register struct MsDirEntry *msd;
  91. {
  92.     msd->msd_Date = OtherEndianWord(msd->msd_Date);
  93.     msd->msd_Time = OtherEndianWord(msd->msd_Time);
  94.     msd->msd_Cluster = OtherEndianWord(msd->msd_Cluster);
  95.     msd->msd_Filesize = OtherEndianLong(msd->msd_Filesize);
  96. }
  97.  
  98. word
  99. ClusterToSector(cluster)
  100. register word    cluster;
  101. {
  102.     return cluster ? Disk.start + cluster * Disk.spc
  103.     : 0;
  104. }
  105.  
  106. word
  107. ClusterOffsetToSector(cluster, offset)
  108. register word    cluster;
  109. register word    offset;
  110. {
  111.     return cluster ? Disk.start + cluster * Disk.spc + offset / Disk.bps
  112.     : 0;
  113. }
  114.  
  115. word
  116. DirClusterToSector(cluster)
  117. register word    cluster;
  118. {
  119.     return cluster ? Disk.start + cluster * Disk.spc
  120.     : Disk.rootdir;
  121. }
  122.  
  123. word
  124. SectorToCluster(sector)
  125. register word    sector;
  126. {
  127.     return sector ? (sector - Disk.start) / Disk.spc
  128.     : 0;
  129. }
  130.  
  131. /*
  132.  * Get the next cluster in a chain. Sort-of checks for special entries.
  133.  */
  134.  
  135. word
  136. NextCluster(cluster)
  137. word cluster;
  138. {
  139.     register word entry;
  140.  
  141.     return (entry = GetFatEntry(cluster)) >= 0xFFF7 ? FAT_EOF : entry;
  142. }
  143.  
  144. word
  145. NextClusteredSector(sector)
  146. word        sector;
  147. {
  148.     word        next = (sector + 1 - Disk.start) % Disk.spc;
  149.  
  150.     if (next == 0) {
  151.     next = NextCluster(SectorToCluster(sector));
  152.     return next != FAT_EOF ? ClusterToSector(next)
  153.         : SEC_EOF;
  154.     } else
  155.     return sector + 1;
  156. }
  157.  
  158. #ifndef READONLY
  159.  
  160. word
  161. FindFreeSector(prev)
  162. word        prev;
  163. {
  164.     word freecluster = FindFreeCluster(SectorToCluster(prev));
  165.  
  166.     return freecluster == FAT_EOF ? SEC_EOF : ClusterToSector(freecluster);
  167. }
  168.  
  169. #endif
  170.  
  171. /*
  172.  * Find a specific sector. The cache list is a Least Recently Used stack:
  173.  * Put it on the head of the cache list. So if it is not used anymore in a
  174.  * long time, it bubbles to the end of the list, getting a higher chance
  175.  * of being trashed for re-use.
  176.  */
  177.  
  178. struct CacheSec *
  179. FindSecByNumber(number)
  180. register int    number;
  181. {
  182.     register struct CacheSec *sec;
  183.     register struct CacheSec *nextsec;
  184.  
  185.     debug(("FindSecByNumber %d", number));
  186.  
  187.     for (sec = (void *) CacheList.mlh_Head;
  188.      nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) {
  189.     if (sec->sec_Number == number) {
  190.         debug((" (%x) %lx\n", sec->sec_Refcount, sec));
  191.         Remove(sec);
  192.         AddHead(&CacheList, &sec->sec_Node);
  193.         return sec;
  194.     }
  195.     }
  196.  
  197.     debug(("; "));
  198.     return NULL;
  199. }
  200.  
  201. struct CacheSec *
  202. FindSecByBuffer(buffer)
  203. byte           *buffer;
  204. {
  205.     return (struct CacheSec *) (buffer - OFFSETOF(CacheSec, sec_Data));
  206. }
  207.  
  208. /*
  209.  * Get a fresh cache buffer. If we are allowed more cache, we just
  210.  * allocate memory. Otherwise, we try to find a currently unused buffer.
  211.  * We start looking at the end of the list, which is the bottom of the LRU
  212.  * stack. If that fails, allocate more memory anyway. Not that is likely
  213.  * anyway, since we currently lock only one sector at a time.
  214.  */
  215.  
  216. struct CacheSec *
  217. NewCacheSector()
  218. {
  219.     register struct CacheSec *sec;
  220.     register struct CacheSec *nextsec;
  221.  
  222.     debug(("NewCacheSector\n"));
  223.  
  224.     if (CurrentCache < MaxCache) {
  225.     if (sec = AllocMem(CacheBlockSize, BufMemType)) {
  226.         goto add;
  227.     }
  228.     }
  229.     for (sec = (void *) CacheList.mlh_TailPred;
  230.      nextsec = (void *) sec->sec_Node.mln_Pred; sec = nextsec) {
  231.     if ((CurrentCache >= MaxCache) && (sec->sec_Refcount == SEC_DIRTY)) {
  232.         FreeCacheSector(sec);       /* Also writes it to disk */
  233.         continue;
  234.     }
  235.     if (sec->sec_Refcount == 0)     /* Implies not SEC_DIRTY */
  236.         return sec;
  237.     }
  238.  
  239.     sec = AllocMem(CacheBlockSize, BufMemType);
  240.  
  241.     if (sec) {
  242. add:
  243.     CurrentCache++;
  244.     AddHead(&CacheList, &sec->sec_Node);
  245.     } else
  246.     error = ERROR_NO_FREE_STORE;
  247.  
  248.     return sec;
  249. }
  250.  
  251. /*
  252.  * Dispose a cached sector, even if it has a non-zero refcount. If it is
  253.  * dirty, write it out.
  254.  */
  255.  
  256. void
  257. FreeCacheSector(sec)
  258. register struct CacheSec *sec;
  259. {
  260.     debug(("FreeCacheSector %d\n", sec->sec_Number));
  261.     Remove(sec);
  262. #ifndef READONLY
  263.     if (sec->sec_Refcount & SEC_DIRTY) {
  264.     PutSec(sec->sec_Number, sec->sec_Data);
  265.     }
  266. #endif
  267.     FreeMem(sec, CacheBlockSize);
  268.     CurrentCache--;
  269. }
  270.  
  271. /*
  272.  * Create an empty cache list
  273.  */
  274.  
  275. void
  276. InitCacheList()
  277. {
  278.     extern struct CacheSec *sec;    /* Of course this does not exist... */
  279.  
  280.     NewList(&CacheList);
  281.     CurrentCache = 0;
  282.     CacheBlockSize = Disk.bps + sizeof (*sec) - sizeof (sec->sec_Data);
  283. }
  284.  
  285. /*
  286.  * Dispose all cached sectors, possibly writing them to disk.
  287.  */
  288.  
  289. void
  290. FreeCacheList()
  291. {
  292.     register struct CacheSec *sec;
  293.  
  294.     debug(("FreeCacheList, %d\n", CurrentCache));
  295.     while (sec = GetHead(&CacheList)) {
  296.     FreeCacheSector(sec);
  297.     }
  298. }
  299.  
  300. /*
  301.  * Do an insertion sort on tosort in the CacheList. Since it changes the
  302.  * location in the list, you must fetch it before calling this routine.
  303.  * The list will become ascending.
  304.  */
  305.  
  306. void
  307. SortSec(tosort)
  308. register struct CacheSec *tosort;
  309. {
  310.     register struct CacheSec *sec;
  311.     struct CacheSec *nextsec;
  312.     register word   secno;
  313.  
  314.     secno = tosort->sec_Number;
  315.     debug(("SortSec %d: ", secno));
  316.  
  317.     for (sec = (void *) CacheList.mlh_Head;
  318.      nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) {
  319.     debug(("%d, ", sec->sec_Number));
  320.     if (sec == tosort) {
  321.         debug(("\n"));
  322.         return;            /* No need to move it away */
  323.     }
  324.     if (sec->sec_Number > secno)
  325.         break;
  326.     }
  327.     /* Insert before sec */
  328.     Remove(tosort);
  329.     Insert(&CacheList, tosort, sec->sec_Node.mln_Pred);
  330.     debug(("\n"));
  331. }
  332.  
  333. /*
  334.  * Write all dirty cache buffers to disk. They are written from highest to
  335.  * lowest, and then the FAT is written out.
  336.  */
  337.  
  338. void
  339. MSUpdate(immediate)
  340. int        immediate;
  341. {
  342.     register struct CacheSec *sec;
  343.     register struct CacheSec *nextsec;
  344.  
  345.     debug(("MSUpdate\n"));
  346.  
  347. #ifndef READONLY
  348.     if (DelayState & DELAY_DIRTY) {
  349.     /*
  350.      * First sort all dirty sectors on block number
  351.      */
  352.     for (sec = (void *) CacheList.mlh_Head;
  353.          nextsec = (void *) sec->sec_Node.mln_Succ; sec = nextsec) {
  354.         if (sec->sec_Refcount & SEC_DIRTY) {
  355.         SortSec(sec);
  356.         }
  357.     }
  358.     /*
  359.      * Then do a second (backward) scan to write them out.
  360.      */
  361.     for (sec = (void *) CacheList.mlh_TailPred;
  362.          nextsec = (void *) sec->sec_Node.mln_Pred; sec = nextsec) {
  363.         if (sec->sec_Refcount & SEC_DIRTY) {
  364.         PutSec(sec->sec_Number, sec->sec_Data);
  365.         sec->sec_Refcount &= ~SEC_DIRTY;
  366.         }
  367.     }
  368.     DelayState &= ~DELAY_DIRTY;
  369.     }
  370.     if (FatDirty) {
  371.     WriteFat();
  372.     }
  373. #endif
  374.  
  375.     if (immediate)
  376.     DelayState = DELAY_RUNNING1;
  377.  
  378.     if (DelayState & DELAY_RUNNING2) {
  379.     StartTimer();
  380.     DelayState &= ~DELAY_RUNNING2;
  381.     } else {            /* DELAY_RUNNING1 */
  382. #ifndef READONLY
  383.     while (TDUpdate() != 0 && RetryRwError(DiskIOReq))
  384.         ;
  385. #endif
  386.     TDMotorOff();
  387.     DelayState = DELAY_OFF;
  388.     }
  389. }
  390.  
  391. /*
  392.  * Start the timer which triggers cache writing and stopping the disk
  393.  * motor.
  394.  */
  395.  
  396. void
  397. StartTimer()
  398. {
  399.     DelayState |= DELAY_RUNNING1 | DELAY_RUNNING2;
  400.  
  401.     if (CheckIO(TimeIOReq)) {
  402.     WaitIO(TimeIOReq);
  403.     TimeIOReq->tr_node.io_Command = TR_ADDREQUEST;
  404.     TimeIOReq->tr_time.tv_secs = 3;
  405.     TimeIOReq->tr_time.tv_micro = 0;
  406.     SendIO(TimeIOReq);
  407.     }
  408. }
  409.  
  410. /*
  411.  * Get a pointer to a logical sector { 0, ..., MS_SECTCNT - 1}. We
  412.  * allocate a buffer and copy the data in, and lock the buffer until
  413.  * FreeSec() is called.
  414.  */
  415.  
  416. byte           *
  417. GetSec(sector)
  418. int        sector;
  419. {
  420.     struct CacheSec *sec;
  421.  
  422. #ifdef HDEBUG
  423.     if (sector == 0) {
  424.     debug(("************ GetSec(0) ***************\n"));
  425.     }
  426. #endif
  427.  
  428.     if (sec = FindSecByNumber(sector)) {
  429.     sec->sec_Refcount++;
  430.  
  431.     return sec->sec_Data;
  432.     }
  433.     if (sec = NewCacheSector()) {
  434.     register struct IOExtTD *req;
  435.  
  436.     sec->sec_Number = sector;
  437.     sec->sec_Refcount = 1;
  438.  
  439.     debug(("GetSec %d\n", sector));
  440.  
  441.     req = DiskIOReq;
  442.     do {
  443.         req->iotd_Req.io_Command = ETD_READ;
  444.         req->iotd_Req.io_Data = (APTR)sec->sec_Data;
  445.         req->iotd_Req.io_Offset = Disk.lowcyl + (long) sector * Disk.bps;
  446.         req->iotd_Req.io_Length = Disk.bps;
  447.         MyDoIO(req);
  448.     } while (req->iotd_Req.io_Error != 0 && RetryRwError(req));
  449.  
  450.     StartTimer();
  451.  
  452.     if (req->iotd_Req.io_Error == 0) {
  453.         return sec->sec_Data;
  454.     }
  455.     error = ERROR_NOT_A_DOS_DISK;
  456.     FreeCacheSector(sec);
  457.     }
  458.     return NULL;
  459. }
  460.  
  461. #ifndef READONLY
  462.  
  463. byte           *
  464. EmptySec(sector)
  465. int        sector;
  466. {
  467.     byte       *buffer;
  468.     register struct CacheSec *sec;
  469.  
  470. #ifdef HDEBUG
  471.     if (sector == 0) {
  472.     debug(("************ EmptySec(0) ***************\n"));
  473.     }
  474. #endif
  475.     if (sec = FindSecByNumber(sector)) {
  476.     sec->sec_Refcount++;
  477.  
  478.     return sec->sec_Data;
  479.     }
  480.     if (sec = NewCacheSector()) {
  481.     sec->sec_Number = sector;
  482.     sec->sec_Refcount = 1;
  483.  
  484.     return sec->sec_Data;
  485.     }
  486.  
  487.     return NULL;
  488. }
  489.  
  490. void
  491. PutSec(sector, data)
  492. int        sector;
  493. byte           *data;
  494. {
  495.     register struct IOExtTD *req;
  496.  
  497.     debug(("PutSec %d\n", sector));
  498.  
  499.     req = DiskIOReq;
  500.     do {
  501.     req->iotd_Req.io_Command = ETD_WRITE;
  502.     req->iotd_Req.io_Data = (APTR) data;
  503.     req->iotd_Req.io_Offset = Disk.lowcyl + (long) sector * Disk.bps;
  504.     req->iotd_Req.io_Length = Disk.bps;
  505.     MyDoIO(req);
  506.     } while (req->iotd_Req.io_Error != 0 && RetryRwError(req));
  507.  
  508.     StartTimer();
  509. }
  510.  
  511. #endif
  512.  
  513. /*
  514.  * Unlock a cached sector. When the usage count drops to zero, which
  515.  * implies it is not dirty, and we are over our cache quota, the sector is
  516.  * freed. Otherwise we keep it for re-use.
  517.  */
  518.  
  519. void
  520. FreeSec(buffer)
  521. byte           *buffer;
  522. {
  523.     register struct CacheSec *sec;
  524.  
  525.     if (sec = FindSecByBuffer(buffer)) {
  526. #ifdef HDEBUG
  527.     if (sec->sec_Number == 0) {
  528.         debug(("************ FreeSec(0) ***************\n"));
  529.     }
  530. #endif
  531.     if (--sec->sec_Refcount == 0) { /* Implies not SEC_DIRTY */
  532.         if (CurrentCache > MaxCache) {
  533.         FreeCacheSector(sec);
  534.         }
  535.     }
  536.     }
  537. }
  538.  
  539. #ifndef READONLY
  540.  
  541. void
  542. MarkSecDirty(buffer)
  543. byte           *buffer;
  544. {
  545.     register struct CacheSec *sec;
  546.  
  547.     if (sec = FindSecByBuffer(buffer)) {
  548.     sec->sec_Refcount |= SEC_DIRTY;
  549.     DelayState |= DELAY_DIRTY;
  550.     StartTimer();
  551.     }
  552. }
  553.  
  554. /*
  555.  * Write out the FAT. Called from MSUpdate(), so don't call it again from
  556.  * here. Don't use precious cache space for it; you could say it has its
  557.  * own private cache already.
  558.  */
  559.  
  560. void
  561. WriteFat()
  562. {
  563.     register int    fat,
  564.             sec;
  565.     int         disksec = Disk.res;      /* First FAT, first sector */
  566.  
  567.     /* Write all FATs */
  568.     for (fat = 0; fat < Disk.nfats; fat++) {
  569.     for (sec = 0; sec < Disk.spf; sec++) {
  570.         PutSec(disksec++, Fat + sec * Disk.bps);
  571.         /* return;           /* Fat STILL dirty! */
  572.     }
  573.     }
  574.     FatDirty = FALSE;
  575. }
  576.  
  577. #endif
  578.  
  579. int
  580. AwaitDFx()
  581. {
  582.     debug(("AwaitDFx\n"));
  583.     if (DosType) {
  584.     static char    dfx[] = "DFx:";
  585.     void           *dfxProc,
  586.                *DeviceProc();
  587.     char        xinfodata[sizeof(struct InfoData) + 3];
  588.     struct InfoData *infoData;
  589.     int        triesleft;
  590.  
  591.     dfx[2] = '0' + UnitNr;
  592.     infoData = (struct InfoData *)(((long)&xinfodata[3]) & ~3L);
  593.  
  594.     for (triesleft = 10; triesleft; triesleft--) {
  595.         debug(("AwaitDFx %d\n", triesleft));
  596.         if ((dfxProc = DeviceProc(dfx)) == NULL)
  597.         break;
  598.  
  599.         dos_packet(dfxProc, ACTION_DISK_INFO, CTOB(infoData));
  600.         debug(("AwaitDFx %lx\n", infoData->id_DiskType));
  601.         if (infoData->id_DiskType == ID_NO_DISK_PRESENT) {
  602.         /* DFx has not noticed yet. Wait a bit. */
  603.         WaitIO(TimeIOReq);
  604.         TimeIOReq->tr_node.io_Command = TR_ADDREQUEST;
  605.         TimeIOReq->tr_time.tv_secs = 0;
  606.         TimeIOReq->tr_time.tv_micro = 750000L;    /* .75 s */
  607.         SendIO(TimeIOReq);
  608.         continue;
  609.         }
  610.         if (infoData->id_DiskType == ID_DOS_DISK) {
  611.         /* DFx: understands it, so it is not for us. */
  612.         return 1;
  613.         }
  614.         /*
  615.          * All (well, most) other values mean that DFx: does not
  616.          * understand it, so we can give it a try.
  617.          */
  618.         break;
  619.     }
  620.     }
  621.     return 0;
  622. }
  623.  
  624. int
  625. ReadBootBlock()
  626. {
  627.     int protstatus;
  628.  
  629.     debug(("ReadBootBlock\n"));
  630.     FreeFat();                  /* before disk parameters change */
  631.     TDClear();
  632.  
  633.     if (TDProtStatus() >= 0) {
  634.     register byte *bootblock;
  635.     short        oldCancel;
  636.  
  637.     oldCancel = Cancel;
  638.  
  639.     if (AwaitDFx())
  640.         goto bad_disk;
  641.     if ((protstatus = TDProtStatus()) < 0)
  642.         goto no_disk;
  643.  
  644.     TDChangeNum();
  645.     debug(("Changenumber = %ld\n", DiskIOReq->iotd_Count));
  646.  
  647.     Cancel = 1;
  648.     if (bootblock = GetSec(0)) {
  649.         word bps;
  650.  
  651.         if (CheckBootBlock &&
  652.                 /* Atari: empty or 68000 JMP */
  653.         /*bootblock[0] != 0x00 && bootblock[0] != 0x4E &&*/
  654.                 /* 8086 ml for a jump */
  655.         bootblock[0] != 0xE9 && bootblock[0] != 0xEB) {
  656.         goto bad_disk;
  657.         }
  658.         bps = Get8086Word(bootblock + 0x0b);
  659.         Disk.spc = bootblock[0x0d];
  660.         Disk.res = Get8086Word(bootblock + 0x0e);
  661.         Disk.nfats = bootblock[0x10];
  662.         Disk.ndirs = Get8086Word(bootblock + 0x11);
  663.         Disk.nsects = Get8086Word(bootblock + 0x13);
  664.         Disk.media = bootblock[0x15];
  665.         Disk.spf = Get8086Word(bootblock + 0x16);
  666.         Disk.spt = Get8086Word(bootblock + 0x18);
  667.         Disk.nsides = Get8086Word(bootblock + 0x1a);
  668.         Disk.nhid = Get8086Word(bootblock + 0x1c);
  669.         FreeSec(bootblock);
  670.  
  671.         /*
  672.          *    Maybe the sector size just changed. Who knows?
  673.          */
  674.         if (Disk.bps != bps) {
  675.         FreeCacheList();
  676.         Disk.bps = bps;
  677.         InitCacheList();
  678.         }
  679.  
  680.         Disk.ndirsects = (Disk.ndirs * MS_DIRENTSIZE) / Disk.bps;
  681.         Disk.rootdir = Disk.res + Disk.spf * Disk.nfats;
  682.         Disk.datablock = Disk.rootdir + Disk.ndirsects;
  683.         Disk.start = Disk.datablock - MS_FIRSTCLUST * Disk.spc;
  684.         /* Available clusters are 2..maxclust in secs start..nsects-1 */
  685.         Disk.maxclst = (Disk.nsects - Disk.start) / Disk.spc - 1;
  686.         Disk.bpc = Disk.bps * Disk.spc;
  687.         Disk.vollabel = FakeRootDirEntry;
  688. /*        Disk.fat16bits = Disk.nsects > 20740;   /* DOS3.2 magic value */
  689.         Disk.fat16bits = Disk.maxclst >= 0xFF7; /* DOS3.0 magic value */
  690.  
  691.         debug(("%x\tbytes per sector\n", Disk.bps));
  692.         debug(("%x\tsectors per cluster\n", Disk.spc));
  693.         debug(("%x\treserved blocks\n", Disk.res));
  694.         debug(("%x\tfats\n", Disk.nfats));
  695.         debug(("%x\tdirectory entries\n", Disk.ndirs));
  696.         debug(("%x\tsectors\n", Disk.nsects));
  697.         debug(("%x\tmedia byte\n", Disk.media));
  698.         debug(("%x\tsectors per FAT\n", Disk.spf));
  699.         debug(("%x\tsectors per track\n", Disk.spt));
  700.         debug(("%x\tsides\n", Disk.nsides));
  701.         debug(("%x\thidden sectors\n", Disk.nhid));
  702.  
  703.         debug(("%x\tdirectory sectors\n", Disk.ndirsects));
  704.         debug(("%x\troot dir block\n", Disk.rootdir));
  705.         debug(("%x\tblock for (imaginary) cluster 0\n", Disk.start));
  706.         debug(("%x\tfirst data block\n", Disk.datablock));
  707.         debug(("%x\tclusters total\n", Disk.maxclst));
  708.         debug(("%x\tbytes per cluster\n", Disk.bpc));
  709.         debug(("%x\t16-bits FAT?\n", Disk.fat16bits));
  710.  
  711.         IDDiskType = ID_DOS_DISK;
  712. #ifdef READONLY
  713.         IDDiskState = ID_WRITE_PROTECTED;
  714. #else
  715.         if (protstatus > 0)
  716.         IDDiskState = ID_WRITE_PROTECTED;
  717.         else
  718.         IDDiskState = ID_VALIDATED;
  719. #endif
  720.  
  721.         if (Disk.nsects / (MS_SPT * Disk.nsides) <= 40)
  722.         DiskIOReq->iotd_Req.io_Flags |= IOMDF_40TRACKS;
  723.         else
  724.         DiskIOReq->iotd_Req.io_Flags &= ~IOMDF_40TRACKS;
  725.  
  726.         GetFat();
  727.     } else {
  728.         debug(("Can't read %d.\n", DiskIOReq->iotd_Req.io_Error));
  729.     bad_disk:
  730.         FreeCacheList();
  731.         error = ERROR_NO_DISK;
  732.         IDDiskType = ID_UNREADABLE_DISK;
  733.         IDDiskState = ID_WRITE_PROTECTED;
  734.     }
  735.     Cancel = oldCancel;
  736.     }
  737. #ifdef HDEBUG
  738.     else debug(("No disk inserted %d.\n", DiskIOReq->iotd_Req.io_Error));
  739. #endif
  740. no_disk:
  741.     return 1;
  742. }
  743.  
  744. /*
  745.  * We try to identify the disk currently in the drive, trying to find the
  746.  * volume label in the first directory block.
  747.  */
  748.  
  749. int
  750. IdentifyDisk(name, date)
  751. char           *name;        /* Should be at least 32 characters */
  752. struct DateStamp *date;
  753. {
  754.     debug(("IdentifyDisk\n"));
  755.     ReadBootBlock();            /* Also sets default vollabel */
  756.  
  757.     if (IDDiskType == ID_DOS_DISK) {
  758.     byte           *dirblock;
  759.     register struct MsDirEntry *dirent;
  760.  
  761.     if (dirblock = GetSec(Disk.rootdir)) {
  762.         dirent = (struct MsDirEntry *) dirblock;
  763.  
  764.         while ((byte *) dirent < &dirblock[Disk.bps]) {
  765.         if (dirent->msd_Attributes & ATTR_VOLUMELABEL) {
  766.             Disk.vollabel.de_Msd = *dirent;
  767.             Disk.vollabel.de_Sector = Disk.rootdir;
  768.             Disk.vollabel.de_Offset = (byte *) dirent - dirblock;
  769.             OtherEndianMsd(&Disk.vollabel.de_Msd);
  770.             Disk.vollabel.de_Msd.msd_Cluster = 0;    /* to be sure */
  771.             break;
  772.         }
  773.         dirent++;
  774.         }
  775.         strncpy(name, Disk.vollabel.de_Msd.msd_Name, 8 + 3);
  776.         name[8 + 3] = '\0';
  777.         ZapSpaces(name, name + 8 + 3);
  778.         ToDateStamp(date, Disk.vollabel.de_Msd.msd_Date,
  779.             Disk.vollabel.de_Msd.msd_Time);
  780.         debug(("Disk is called '%s'\n", name));
  781.  
  782.         FreeSec(dirblock);
  783.  
  784.         return 0;
  785.     }
  786.     }
  787.     return 1;
  788. }
  789.  
  790. /*
  791.  * Remove the disk change SoftInt. The V1.2 / V1.3 version is broken, so
  792.  * we use a workaround. The correct thing to do is shown but not used.
  793.  */
  794.  
  795. void
  796. TDRemChangeInt()
  797. {
  798.     if (DiskChangeReq) {
  799.     register struct IOExtTD *req = DiskIOReq;
  800.  
  801. #if 0                /* V1.2 and V1.3 have a broken
  802.                  * TD_REMCHANGEINT */
  803.     req->iotd_Req.io_Command = TD_REMCHANGEINT;
  804.     req->iotd_Req.io_Data = (void *) DiskChangeReq;
  805.     MyDoIO(req);
  806.     WaitIO(DiskChangeReq);
  807. #else
  808.     Forbid();
  809.     Remove(DiskChangeReq);
  810.     Permit();
  811. #endif
  812.     DeleteExtIO(DiskChangeReq);
  813.     DiskChangeReq = NULL;
  814.     }
  815. }
  816.  
  817. /*
  818.  * Set the disk change SoftInt. Return nonzero on failure.
  819.  */
  820.  
  821. int
  822. TDAddChangeInt(interrupt)
  823. struct Interrupt *interrupt;
  824. {
  825.     register struct IOExtTD *req = DiskIOReq;
  826.  
  827.     if (DiskChangeReq) {
  828.     TDRemChangeInt();
  829.     }
  830.     DiskChangeReq = (void *)CreateExtIO(DiskReplyPort,
  831.                      (long) sizeof (*DiskChangeReq));
  832.     if (DiskChangeReq) {
  833.     /* Clone IO request part */
  834.     DiskChangeReq->io_Device = req->iotd_Req.io_Device;
  835.     DiskChangeReq->io_Unit = req->iotd_Req.io_Unit;
  836.     DiskChangeReq->io_Command = TD_ADDCHANGEINT;
  837.     DiskChangeReq->io_Data = (void *) interrupt;
  838.     SendIO(DiskChangeReq);
  839.  
  840.     return 0;
  841.     }
  842.     return 1;
  843. }
  844.  
  845. /*
  846.  * Get the current disk change number. Necessary for ETD_ commands. Makes
  847.  * absolutely sure nobody can change the disk without us noticing it.
  848.  */
  849.  
  850. int
  851. TDChangeNum()
  852. {
  853.     register struct IOExtTD *req = DiskIOReq;
  854.  
  855.     req->iotd_Req.io_Command = TD_CHANGENUM;
  856.     MyDoIO(req);
  857.     req->iotd_Count = req->iotd_Req.io_Actual;
  858.  
  859.     return req->iotd_Req.io_Actual;
  860. }
  861.  
  862. /*
  863.  * Get the current write protection state.
  864.  *
  865.  * Zero means writable, one means write protected, minus one means
  866.  * no disk in drive.
  867.  */
  868.  
  869. int
  870. TDProtStatus()
  871. {
  872.     register struct IOExtTD *req = DiskIOReq;
  873.  
  874.     req->iotd_Req.io_Command = TD_PROTSTATUS;
  875.     MyDoIO(req);
  876.  
  877.     if (req->iotd_Req.io_Error)
  878.     return -1;
  879.  
  880.     return req->iotd_Req.io_Actual != 0;
  881. }
  882.  
  883. /*
  884.  * Switch the drive motor off. Return previous state. Don't use this when
  885.  * you have allocated the disk via GetDrive().
  886.  */
  887.  
  888. int
  889. TDMotorOff()
  890. {
  891.     register struct IOExtTD *req = DiskIOReq;
  892.  
  893.     req->iotd_Req.io_Command = TD_MOTOR;
  894.     req->iotd_Req.io_Length = 0;
  895.     MyDoIO(req);
  896.  
  897.     return req->iotd_Req.io_Actual;
  898. }
  899.  
  900. /*
  901.  * Clear all internal messydisk buffers.
  902.  */
  903.  
  904. int
  905. TDClear()
  906. {
  907.     register struct IOExtTD *req = DiskIOReq;
  908.  
  909.     req->iotd_Req.io_Command = CMD_CLEAR;
  910.  
  911.     return MyDoIO(req);
  912. }
  913.  
  914. #ifndef READONLY
  915. /*
  916.  * Write out all internal messydisk buffers to the disk.
  917.  */
  918.  
  919. int
  920. TDUpdate()
  921. {
  922.     register struct IOExtTD *req = DiskIOReq;
  923.  
  924.     req->iotd_Req.io_Command = ETD_UPDATE;
  925.  
  926.     return MyDoIO(req);
  927. }
  928. #endif
  929.  
  930. int
  931. MyDoIO(ioreq)
  932. register struct IOStdReq *ioreq;
  933. {
  934.     ioreq->io_Flags |= IOF_QUICK;    /* Preserve IOMDF_40TRACKS */
  935.     BeginIO(ioreq);
  936.     return WaitIO(ioreq);
  937. }
  938.